package fr.insalyon.sims.broadcast.image;

import fr.insalyon.sims.broadcast.network.DataEvent;
import fr.insalyon.sims.broadcast.network.DataListener;
import fr.insalyon.sims.broadcast.network.IStreamTransmitter;

public class ImageOutputBuffer implements DataListener {
	// ------------------------------------------------------- CONSTANTS -- //
	// ------------------------------------------------------ ATTRIBUTES -- //
	
	// Flux sur lequel l'image est émise
	private IStreamTransmitter transmitter;
	
	// Identifiant de l'image en cours de réception
	private long currentImageId;
	
	// Image en cours de réception
	private RawImage currentImage;
	
	// Image déjà réceptionnée en attente de réception de paquets en retard
	private RawImage previousImage;
	
	// ---------------------------------------------------- CONSTRUCTORS -- //
	
	public ImageOutputBuffer(IStreamTransmitter transmitter) {
		this.currentImageId = -1;
		this.transmitter = transmitter;
	}
	
	// --------------------------------------------------------- METHODS -- //
	
	public void dataReceived(DataEvent event) {
		byte[] data = event.getData();
		
		// Read packet header
		int imageId = 0;
		for (int i = 0; i < 4; i++)
			imageId |= (data[i] & 0xff) << (8 * (3 - i));
		
		int imageSize = 0;
		for (int i = 0; i < 4; i++)
			imageSize |= (data[i + 4] & 0xff) << (8 * (3 - i));
		
		int packetPosition = 0;
		for (int i = 0; i < 4; i++)
			packetPosition |= (data[i + 8] & 0xff) << (8 * (3 - i));
		
		int packetSize = 0;
		for (int i = 0; i < 4; i++)
			packetSize |= (data[i + 12] & 0xff) << (8 * (3 - i));
		
		int offset = 16;
		
		if (currentImageId == -1)
			currentImageId = imageId;
		
		if (imageId == currentImageId) {
			// Construit une nouvelle image si c'est le premier packet de
			// l'image
			if (currentImage == null)
				currentImage = new RawImage(imageSize + 24);
			
			// Fragment de l'image la plus récente reçue sur le flux
			currentImage.addFragment(data, offset, packetSize, packetPosition);
		} else if (imageId > currentImageId) {
			if (previousImage != null) {
				fireNewImage(previousImage);
				previousImage = null;
			}
			
			// On a recu un fragment de l'image suivante alors qu'on attendait
			// un fragment de l'image précédente
			previousImage = currentImage;
			currentImage = new RawImage(imageSize + 24);
			currentImageId = imageId;
			
			currentImage.addFragment(data, offset, packetSize, packetPosition);
		} else if (imageId < currentImageId) {
			// Fragment de l'image précédente
			previousImage.addFragment(data, offset, packetSize, packetPosition);
			
			// Image complète
			if (previousImage.isComplete()) {
				fireNewImage(previousImage);
				previousImage = null;
			}
		}
		
		// Teste si l'image est complète
		if (currentImage.isComplete()) {
			// Envoie l'image précédente si l'image en cours est complète
			if (previousImage != null)
				fireNewImage(previousImage);
			fireNewImage(currentImage);
			
			previousImage = null;
			currentImage = null;
			currentImageId = imageId;
		}
	}
	
	// ------------------------------------------------- PRIVATE METHODS -- //
	
	protected void fireNewImage(RawImage image) {
		if (image.isComplete()) {
			byte[] data = image.getData();
			this.transmitter.send(data, 0, data.length);
		} else {
			
		}
	}
	
	// --------------------------------------------------- INNER CLASSES -- //
}
